#include "libcomm.h"

#ifdef __GNUC__
#include <iconv.h>  
static void _Convert(const char *from_charset, const char *to_charset, const String& in_buffer, String& out_buffer)  
{  
	iconv_t cd; 
	String inbuffer = in_buffer; // const cast.
	char* pin_ = (char*)inbuffer;
	char* pout_ = (char*)out_buffer;
	char **pin = &pin_;  
	char **pout = &pout_;  
	size_t inlen = inbuffer.size();
	size_t outlen = out_buffer.size();

	cd = iconv_open(to_charset,from_charset);  
	MYLIB_ASSERT_ERRNO((iconv_t)-1 != cd);
	MYLIB_ASSERT_ERRNO((size_t)-1 != iconv(cd,pin,&inlen,pout,&outlen));
	MYLIB_ASSERT(0 == inlen && outlen >= 0);// the output buffer is too small.

	out_buffer.resize(out_buffer.size() - outlen);
 
	iconv_close(cd);  
} 
#endif

#ifdef COMM_MSVC

static char* _Unicode_Ansi(const wchar_t* src,unsigned code_page)
{
	int len;
	char* str;
	len = WideCharToMultiByte(code_page, 0, src, -1, NULL, 0, NULL, NULL); 
	if(0 == len)
	{
		LOGFUNCERROR(WideCharToMultiByte);
		MYLIB_ASSERT(len>0);
	}
	str = (char*)Buffer::malloc(len+1);
	MYLIB_ASSERT_NOTNULL(str);
	WideCharToMultiByte(code_page, 0, src, -1, str, len, NULL, NULL);   
	str[len] = 0; 
	return str;  
}

static wchar_t* _Ansi_Unicode(const char* src,unsigned code_page)
{
	int len;
	wchar_t* wstr;
	len = MultiByteToWideChar(code_page, 0, src, -1, NULL, 0);  
	if(0 == len)
	{
		LOGFUNCERROR(MultiByteToWideChar);
		MYLIB_ASSERT(len>0);
	}
	wstr = (wchar_t*)Buffer::malloc((len+1)*sizeof(wchar_t));  
	MYLIB_ASSERT_NOTNULL(wstr);
	MultiByteToWideChar(code_page, 0, src, -1, wstr, len); 
	wstr[len] = 0;
	return wstr;
}

static char* _Unicode_GB2312(const wchar_t* src)
{
	return _Unicode_Ansi(src,CP_ACP);
}

static wchar_t* _GB2312_Unicode(const char* src)
{
	return _Ansi_Unicode(src,CP_ACP);
}

static char* _Unicode_UTF8(const wchar_t* src)
{
	return _Unicode_Ansi(src,CP_UTF8);
}

static wchar_t* _UTF8_Unicode(const char* src)
{
	return _Ansi_Unicode(src,CP_UTF8);
}

#endif

void CodecUTF8::UTF8_GB2312(const String& utf8, String& gb2312)
{
#ifdef COMM_MSVC
	char* str;
	wchar_t* wstr;
	wstr = _UTF8_Unicode(utf8);
	str = _Unicode_GB2312(wstr);
	{
		gb2312 = str;
	}
	Buffer::free(wstr);
	Buffer::free(str);
#else
	_Convert("utf-8", "gb2312", utf8, gb2312);
#endif
}

void CodecUTF8::GB2312_UTF8(const String& gb2312, String& utf8)
{
#ifdef COMM_MSVC
	char* str;
	wchar_t* wstr;
	wstr = _GB2312_Unicode(gb2312);
	str = _Unicode_UTF8(wstr);
	{
		utf8 = str;
	}
	Buffer::free(wstr);
	Buffer::free(str);
#else
	_Convert("gb2312", "utf-8", gb2312, utf8);
#endif
}

#ifdef COMM_MSVC

void CodecUTF8::UTF8_UNICODE(const String& utf8, Buffer& unicode)
{
	wchar_t* wstr;
	wstr = _UTF8_Unicode(utf8);
	{
		int length = wcslen(wstr);
		unicode.resize((length+1)*sizeof(wchar_t));
		wcsncpy((wchar_t*)unicode.get(),wstr,length);
	}
	Buffer::free(wstr);
}

void CodecUTF8::GB2312_UNICODE(const String& gb2312, Buffer& unicode)
{
	wchar_t* wstr;
	wstr = _GB2312_Unicode(gb2312);
	{
		int length = wcslen(wstr);
		unicode.resize((length+1)*sizeof(wchar_t));
		wcsncpy((wchar_t*)unicode.get(),wstr,length);
	}
	Buffer::free(wstr);
}

void CodecUTF8::UNICODE_UTF8(const Buffer& unicode,String& utf8)
{
	char* str;
	str = _Unicode_UTF8((wchar_t*)unicode.get());
	{
		utf8 = str;
	}
	Buffer::free(str);
}

void CodecUTF8::UNICODE_GB2312(const Buffer& unicode, String& gb2312)
{
	char* str;
	str = _Unicode_GB2312((wchar_t*)unicode.get());
	{
		gb2312 = str;
	}
	Buffer::free(str);
}

#endif

String CodecUTF8::FromGB2312(const char* gb2312)
{
	String utf8;
	if(NULL != gb2312 && strlen(gb2312)>0)
	{
		utf8.resize(2 * (strlen(gb2312)+1));
		CodecUTF8::GB2312_UTF8(gb2312,utf8);
	}
	return utf8.get();
}

String CodecUTF8::ToGB2312(const char* utf8)
{
	String gb2312;
	if(NULL != utf8 && strlen(utf8)>0)
	{
		gb2312.resize(strlen(utf8)+1);
		CodecUTF8::UTF8_GB2312(utf8,gb2312);
	}
	return gb2312.get();
}